<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>XML工具</title>
    <style>
        /* 基础样式重置 */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        /* CSS变量定义 */
        :root {
            --primary-color: #3498db;
            --primary-hover: #2980b9;
            --secondary-color: #95a5a6;
            --secondary-hover: #7f8c8d;
            --background-color: #f4f4f4;
            --card-background: #ffffff;
            --text-color: #333333;
            --border-color: #dddddd;
            --border-radius: 5px;
            --box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            --font-family: Arial, sans-serif;
            --monospace-font: 'Courier New', monospace;
            --gap: 20px;
        }
        
        body {
            font-family: var(--font-family);
            background-color: var(--background-color);
            color: var(--text-color);
            margin: 0;
            padding: 20px;
        }
        
        .container {
            max-width: 95%;
            margin: 0 auto;
        }
        
        /* 头部样式 */
        header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        
        h1 {
            color: #2c3e50;
            font-size: 24px;
        }
        
        .back-btn {
            background-color: var(--primary-color);
            color: white;
            padding: 10px 20px;
            text-decoration: none;
            border-radius: var(--border-radius);
            transition: background-color 0.3s ease;
        }
        
        .back-btn:hover {
            background-color: var(--primary-hover);
        }
        
        /* 工具卡片样式 */
        .tool-card {
            background-color: var(--card-background);
            padding: 20px;
            border-radius: 8px;
            box-shadow: var(--box-shadow);
        }
        
        /* 按钮组样式 */
        .btn-group {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            flex-wrap: wrap;
        }
        
        .btn {
            background-color: var(--primary-color);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: var(--border-radius);
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.3s ease;
        }
        
        .btn:hover {
            background-color: var(--primary-hover);
        }
        
        .btn:active {
            transform: translateY(1px);
        }
        
        .btn-secondary {
            background-color: var(--secondary-color);
        }
        
        .btn-secondary:hover {
            background-color: var(--secondary-hover);
        }
        
        /* 输入输出容器样式 */
        .io-container {
            display: flex;
            gap: 20px;
            align-items: flex-start;
        }
        
        .io-panel {
            flex: 1;
            min-width: 0;
        }
        
        .io-header {
            font-weight: bold;
            margin-bottom: 10px;
            color: #555;
        }
        
        /* 文本区域样式 */
        .textarea-wrapper {
            position: relative;
            height: 500px;
        }
        
        /* 行号容器样式 */
        .lined-area {
            position: relative;
            display: flex;
            width: 100%;
            height: 100%;
            overflow: hidden;
        }
        
        /* 行号区域样式 */
        .line-numbers {
            background-color: #f0f0f0;
            color: #808080;
            text-align: right;
            padding: 15px 8px;
            border-right: 1px solid #ddd;
            user-select: none;
            font-family: var(--monospace-font);
            font-size: 14px;
            line-height: 1.5;
            overflow: hidden;
            min-width: 40px;
            white-space: pre;
            flex-shrink: 0;
            overflow-y: hidden;
        }
        
        /* 内容区域样式 */
        .content-area {
            flex: 1;
            padding: 15px;
            overflow: auto;
            font-family: var(--monospace-font);
            font-size: 14px;
            line-height: 1.5;
            background-color: #fafafa;
        }
        
        /* 高亮内容样式 */
        .highlighted-content {
            margin: 0;
            white-space: pre;
        }
        
        /* 文本区域容器 */
        .textarea-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            box-sizing: border-box;
        }
        
        textarea {
            width: 100%;
            height: 100%;
            padding: 15px 15px 15px 58px;
            border: 1px solid var(--border-color);
            border-radius: var(--border-radius);
            font-family: var(--monospace-font);
            font-size: 14px;
            resize: vertical;
            line-height: 1.5;
            overflow: auto;
            box-sizing: border-box;
        }
        
        /* 交换按钮样式 */
        .swap-container {
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 10px;
        }
        
        .swap-btn {
            width: 50px;
            height: 50px;
            border-radius: 50%;
            font-size: 20px;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 0;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .swap-btn:hover {
            transform: scale(1.1);
        }
        
        /* 底部操作按钮样式 */
        .action-buttons {
            display: flex;
            justify-content: center;
            gap: 10px;
            margin-top: 20px;
        }
        
        /* XML语法高亮样式 */
        .xml-tag {
            color: #0000ff;
        }
        
        .xml-attribute {
            color: #a31515;
        }
        
        .xml-value {
            color: #006400;
        }
        
        .xml-comment {
            color: #808080;
            font-style: italic;
        }
        
        .xml-processor {
            color: #808080;
        }
        
        /* 响应式设计 */
        @media (max-width: 768px) {
            .io-container {
                flex-direction: column;
            }
            
            .btn-group {
                flex-direction: column;
            }
            
            textarea,
            .highlighted-area {
                height: 300px;
            }
            
            .swap-container {
                padding: 10px 0;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>XML工具</h1>
            <a href="index.html" class="back-btn">返回主页</a>
        </header>
        
        <div class="tool-card">
            <!-- 工具按钮组 -->
            <div class="btn-group">
                <button class="btn" onclick="compressXML()">压缩XML</button>
                <button class="btn" onclick="formatXML()">格式化XML</button>
                <button class="btn" onclick="compressAndEscapeXML()">压缩并添加转义字符</button>
                <button class="btn" onclick="unescapeXML()">去转义字符</button>
                <button class="btn" onclick="unescapeAndFormatXML()">去转义并格式化</button>
            </div>
            
            <!-- 输入输出区域 -->
            <div class="io-container">
                <!-- 输入区域 -->
                <div class="io-panel">
                    <div class="io-header">输入XML：</div>
                    <div class="textarea-wrapper">
                        <textarea id="inputArea" placeholder="请输入XML内容..."></textarea>
                    </div>
                </div>
                
                <!-- 交换按钮 -->
                <div class="swap-container">
                    <button class="btn swap-btn" onclick="swapInputOutput()">⇄</button>
                </div>
                
                <!-- 输出区域 -->
                <div class="io-panel">
                    <div class="io-header" style="display: flex; justify-content: space-between; align-items: center;">
                        输出结果：
                        <button class="btn btn-secondary" style="padding: 5px 10px; font-size: 12px;" onclick="copyResult()">复制</button>
                    </div>
                    <div class="textarea-wrapper">
                        <!-- 文本区域容器 -->
                        <div id="outputTextAreaContainer" class="textarea-container">
                            <textarea id="outputArea" readonly placeholder="处理结果将显示在这里..."></textarea>
                        </div>
                        <!-- 带行号的高亮区域 -->
                        <div id="highlightedArea" class="lined-area" style="display: none;">
                            <div id="lineNumbers" class="line-numbers"></div>
                            <div class="content-area">
                                <pre id="highlightedContent" class="highlighted-content"></pre>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            
            <!-- 底部操作按钮 -->
            <div class="action-buttons">
                <button class="btn btn-secondary" onclick="copyResult()">复制结果</button>
                <button class="btn btn-secondary" onclick="clearAll()">清空所有</button>
            </div>
        </div>
    </div>
    
    <script>
        // 通用函数：交换输入输出
        function swapInputOutput() {
            const input = document.getElementById('inputArea');
            const output = document.getElementById('outputArea');
            const highlighted = document.getElementById('highlightedArea');
            
            const temp = input.value;
            input.value = output.value;
            updateOutput(output.value);
            output.value = temp;
        }
        
        // 通用函数：复制结果
        function copyResult() {
            const output = document.getElementById('outputArea');
            output.select();
            
            try {
                if (navigator.clipboard && window.isSecureContext) {
                    navigator.clipboard.writeText(output.value).then(() => {
                        alert('结果已复制到剪贴板');
                    }).catch(err => {
                        document.execCommand('copy');
                        alert('结果已复制到剪贴板');
                    });
                } else {
                    document.execCommand('copy');
                    alert('结果已复制到剪贴板');
                }
            } catch (err) {
                alert('复制失败，请手动复制内容');
            }
        }
        
        // 通用函数：清空所有
        function clearAll() {
            document.getElementById('inputArea').value = '';
            updateOutput('');
        }
        
        // 生成行号
        function generateLineNumbers(content) {
            const lines = content.split('\n');
            let lineNumbers = '';
            for (let i = 1; i <= lines.length; i++) {
                lineNumbers += i + '\n';
            }
            return lineNumbers;
        }
        
        // 通用函数：更新输出结果
        function updateOutput(content) {
            const outputArea = document.getElementById('outputArea');
            const outputTextAreaContainer = document.getElementById('outputTextAreaContainer');
            const highlightedArea = document.getElementById('highlightedArea');
            const lineNumbers = document.getElementById('lineNumbers');
            const highlightedContent = document.getElementById('highlightedContent');
            const contentArea = document.querySelector('.content-area');
            
            outputArea.value = content;
            
            if (content) {
                // 显示高亮结果，隐藏textarea
                const highlighted = highlightXML(content);
                
                // 生成行号
                const lines = content.split('\n');
                let lineNumbersText = '';
                for (let i = 1; i <= lines.length; i++) {
                    lineNumbersText += i + '\n';
                }
                lineNumbers.textContent = lineNumbersText;
                
                // 更新高亮内容
                highlightedContent.innerHTML = highlighted;
                
                // 显示高亮区域，隐藏textarea
                outputTextAreaContainer.style.display = 'none';
                highlightedArea.style.display = 'flex';
                
                // 添加滚动事件监听，确保行号与内容同步滚动
                contentArea.addEventListener('scroll', function() {
                    lineNumbers.scrollTop = contentArea.scrollTop;
                });
            } else {
                // 显示textarea，隐藏高亮结果
                outputTextAreaContainer.style.display = 'block';
                highlightedArea.style.display = 'none';
            }
        }
        
        // XML语法高亮函数
        function highlightXML(xml) {
            if (!xml) return '';
            
            // 创建临时元素用于安全转义
            const tempDiv = document.createElement('div');
            tempDiv.textContent = xml;
            const escapedXml = tempDiv.innerHTML;
            
            // 高亮注释
            let highlighted = escapedXml.replace(/(&lt;!--[\s\S]*?--&gt;)/g, '<span class="xml-comment">$1</span>');
            
            // 高亮处理指令
            highlighted = highlighted.replace(/(&lt;\?[\s\S]*?\?&gt;)/g, '<span class="xml-processor">$1</span>');
            
            // 高亮CDATA
            highlighted = highlighted.replace(/(&lt;!\[CDATA\[[\s\S]*?\]\]&gt;)/g, '<span class="xml-processor">$1</span>');
            
            // 高亮标签
            highlighted = highlighted.replace(/(&lt;\/?)([a-zA-Z0-9_:-]+)(?=[^>]*&gt;)/g, '$1<span class="xml-tag">$2</span>');
            
            // 高亮属性
            highlighted = highlighted.replace(/([a-zA-Z0-9_:-]+)(=)(?=[^>]*&gt;)/g, '<span class="xml-attribute">$1</span>$2');
            
            // 高亮属性值
            highlighted = highlighted.replace(/(=)(&quot;.*?&quot;|&#39;.*?&#39;)(?=[^>]*&gt;)/g, '$1<span class="xml-value">$2</span>');
            
            return highlighted;
        }
        
        // 压缩XML
        function compressXML() {
            const input = document.getElementById('inputArea').value;
            if (!input) {
                alert('请输入XML内容');
                return;
            }
            
            // 移除所有空格、换行和制表符，但保留标签内的必要空格
            const compressed = input
                .replace(/\s+/g, ' ') // 替换所有空白字符为单个空格
                .replace(/\s*<\s*/g, '<') // 移除标签前的空格
                .replace(/\s*>\s*/g, '>') // 移除标签后的空格
                .trim();
            
            updateOutput(compressed);
        }
        
        // 格式化XML
        function formatXML() {
            const input = document.getElementById('inputArea').value;
            if (!input) {
                alert('请输入XML内容');
                return;
            }
            
            // 移除所有多余的空格和换行
            let compressed = input
                .replace(/\s+/g, ' ') // 替换所有空白字符为单个空格
                .replace(/\s*<\s*/g, '<') // 移除标签前的空格
                .replace(/\s*>\s*/g, '>') // 移除标签后的空格
                .trim();
            
            // 初始化结果和缩进
            let formatted = '';
            let indent = 0;
            
            // 分割标签
            const tags = compressed.split(/(<[^>]+>)/);
            
            tags.forEach(tag => {
                if (!tag) return;
                
                if (tag.startsWith('</')) {
                    // 结束标签
                    indent = Math.max(0, indent - 1);
                    formatted += '\n' + '  '.repeat(indent) + tag;
                } else if (tag.endsWith('/>')) {
                    // 自闭合标签
                    formatted += '\n' + '  '.repeat(indent) + tag;
                } else if (tag.startsWith('<?') || tag.startsWith('<!')) {
                    // 处理指令或注释
                    formatted += '\n' + '  '.repeat(indent) + tag;
                } else if (tag.startsWith('<')) {
                    // 开始标签
                    formatted += '\n' + '  '.repeat(indent) + tag;
                    indent++;
                } else if (tag.trim()) {
                    // 文本内容
                    formatted += tag.trim();
                }
            });
            
            // 清理多余的换行和空格
            formatted = formatted.replace(/\n\s*\n/g, '\n');
            formatted = formatted.replace(/^\s+/, '');
            formatted = formatted.replace(/\s+$/, '');
            
            updateOutput(formatted);
        }
        
        // 压缩并添加转义字符
        function compressAndEscapeXML() {
            const input = document.getElementById('inputArea').value;
            if (!input) {
                alert('请输入XML内容');
                return;
            }
            
            // 先压缩
            const compressed = input
                .replace(/\s+/g, ' ') // 替换所有空白字符为单个空格
                .replace(/\s*<\s*/g, '<') // 移除标签前的空格
                .replace(/\s*>\s*/g, '>') // 移除标签后的空格
                .trim();
            
            // 添加斜杠转义字符（只转义单引号、双引号和反斜杠）
            const escaped = compressed
                .replace(/\\/g, '\\\\')
                .replace(/"/g, '\\"')
                .replace(/'/g, "\\'");
            
            updateOutput(escaped);
        }
        
        // 去转义字符
        function unescapeXML() {
            const input = document.getElementById('inputArea').value;
            if (!input) {
                alert('请输入XML内容');
                return;
            }
            
            const unescaped = input
                .replace(/\\"/g, '"')
                .replace(/\\'/g, "'")
                .replace(/\\\\/g, '\\');
            
            updateOutput(unescaped);
        }
        
        // 去转义并格式化
        function unescapeAndFormatXML() {
            const input = document.getElementById('inputArea').value;
            if (!input) {
                alert('请输入XML内容');
                return;
            }
            
            // 先去转义
            const unescaped = input
                .replace(/\\"/g, '"')
                .replace(/\\'/g, "'")
                .replace(/\\\\/g, '\\');
            
            // 然后格式化
            // 首先压缩XML，去除多余的空格和换行
            const compressed = unescaped
                .replace(/\s+/g, ' ') // 替换所有空白字符为单个空格
                .replace(/\s*<\s*/g, '<') // 移除标签前的空格
                .replace(/\s*>\s*/g, '>') // 移除标签后的空格
                .trim();
            
            let formatted = '';
            let indent = 0;
            let inTag = false;
            let tagContent = '';
            
            for (let i = 0; i < compressed.length; i++) {
                const char = compressed[i];
                
                if (char === '<') {
                    inTag = true;
                    tagContent = '<';
                    
                    // 在标签开始前添加换行和缩进
                    if (i > 0 && compressed[i - 1] === '>') {
                        formatted += '\n' + '  '.repeat(indent);
                    }
                } else if (char === '>') {
                    tagContent += '>';
                    inTag = false;
                    
                    // 检查标签类型
                    if (tagContent.startsWith('</')) {
                        // 结束标签，先减少缩进
                        indent = Math.max(0, indent - 1);
                        formatted += '  '.repeat(indent) + tagContent;
                    } else if (tagContent.endsWith('/>')) {
                        // 自闭合标签，不改变缩进
                        formatted += tagContent;
                    } else if (tagContent.startsWith('<?') || tagContent.startsWith('<!')) {
                        // 处理指令或注释，不改变缩进
                        formatted += tagContent;
                    } else {
                        // 开始标签，先添加标签，然后增加缩进
                        formatted += tagContent;
                        indent++;
                    }
                } else if (inTag) {
                    tagContent += char;
                } else {
                    // 文本内容，直接添加
                    formatted += char;
                }
            }
            
            // 清理多余的换行和空格
            formatted = formatted
                .replace(/\n\s*\n/g, '\n')
                .replace(/^\s+/, '')
                .replace(/\s+$/, '');
            
            updateOutput(formatted);
        }
    </script>
</body>
</html>